import org.jbox2d.common.Vec2;
import pulpcore.animation.BindFunction;
import pulpcore.animation.Bool;
import pulpcore.animation.Fixed;
import pulpcore.sound.Playback;
import pulpcore.sound.Sound;
import pulpcore.sprite.Sprite;
import pulpfizz.physics.BodyUtils;
import pulpfizz.physics.Material;
import pulpfizz.pulp.body.Java2DBodySprite;

import java.awt.*;

/**
 * The main player-controllable object.
 * Created by will
 * Date: Apr 30, 2008 7:29:27 PM
 */
public class Player extends Java2DBodySprite {

    Sprite shadow;

    Playback sound;
    static Sound plop = Sound.load("plop2.ogg");

    public static final double SHOT_SPEED = 1500;
    public static final double SHOT_RADIUS = 0.2;

    static Material m = new Material(5,5,10,10,10);
    private float r; // Player's radius
    protected float recoil;
    public ChargeSprite charge;

    public Player(Arena physics, double x, double y, double r, float sharpness) {
        super(BodyUtils.createCircle(physics.getWorld(), (float)x, (float)y, (float)r), physics, sharpness, Color.white,Color.red,Color.BLACK);
        this.r = (float)r;
//        m.applyAll(body);
        body.allowSleeping(false);
        body.setBullet(true);

        // Sprite for charging effect
        charge = new ChargeSprite(this.x.getAsInt(),this.y.getAsInt(),width.getAsInt()*2,height.getAsInt()*2,this);
        charge.setAnchor(Sprite.CENTER);
        physics.add(charge);

        Color greyshadow = new Color(50, 50, 50, 100);
        shadow = new Java2DBodySprite(body,physics,sharpness, greyshadow,greyshadow,greyshadow);
//        shadow = new FilledSprite(this.x.get(),this.y.get(),this.width.get(),this.height.get(), Colors.rgba(Colors.BLACK,100));
        shadow.x.bindTo(posX());
        shadow.y.bindTo(posY());
        shadow.bindSizeTo(this);
        shadow.angle.bindTo(this.angle);
        shadow.alpha.bindTo(this.alpha);
    }

    public void fireShot(double targetX, double targetY) {
        double speed = SHOT_SPEED * shotCharge * shotCharge;
        float shotRadius = (float)(SHOT_RADIUS * Math.sqrt(shotCharge));
        if(recoil > 0) return;
        recoil = 200;
//        CoreSystem.print("shot fired, targetX="+targetX+",y="+targetY+",speed="+speed);
        // Set shot origin to outer edge of the player, in the direction of the shot fired
        Vec2 shotVec = body.getPosition();
        Vec2 targetVec = new Vec2((float)targetX,(float)targetY);
        Vec2 transVec = targetVec.sub(shotVec);
        transVec.normalize();
        transVec.mulLocal(r + shotRadius / 2);
        shotVec.addLocal(transVec);

//        Shot shot = getArena().getNearestShot(this.x.get(),this.y.get(),30);
        Shot shot;
        if(x.get() < getArena().width.get() / 2) {
            shot = getArena().getPooledShot(x.get()-60,y.get()-40,60,80);
        } else {
            shot = getArena().getPooledShot(x.get(),y.get()-40,60,80);
        }
        if(shot != null) {
            // Shot actually fired!
            getArena().remove(shot);
            physics.add(new Shot(physics,shotVec.x, shotVec.y, shotRadius,targetVec,speed));
            Fixed pan = new Fixed(this.x.get() / physics.width.get() * 2 - 1);
            plop.play(new Fixed(1),pan);
            // Add a recoil of the same force to the player.
            Vec2 recoilVec = transVec.negate();
            this.push(recoilVec.x,recoilVec.y,speed);
            shotCharge = 1;
        }
    }
    public Bool canFire = new Bool(false);
    public boolean charging = true;
    public Fixed shotCharged = new Fixed(1);
    public double shotCharge = 1;

    public Arena getArena() {
        return (Arena)physics;
    }
    public void update(int elapsedTime) {
        super.update(elapsedTime);
        recoil -= elapsedTime;
        if(recoil < 0) recoil = 0;
        if(charging && canFire()) shotCharge += ((double)elapsedTime / 1000);
        if(shotCharge > 3) shotCharge = 3;
        charge.width.set(this.width.get() * shotCharge);
        charge.height.set(this.height.get() * shotCharge);
        shotCharged.set(shotCharge * Math.PI*2 + angle.get());
    }

    public void chargeShot() {
        charging = true;
    }
    public void fireChargedShot(double targetX, double targetY, double speed, double rad) {
        fireShot(targetX,targetY);
        shotCharge = 1;
        charging = false;
    }

    BindFunction posX() {
        return new BindFunction() {
            public Number f() {
                return (posX(body).f().doubleValue() +3);
            }
        };
    }
    BindFunction posY() {
        return new BindFunction() {
            public Number f() {
                return (posY(body).f().doubleValue() +1);
            }
        };
    }

    public Sprite getShadow() {
        return shadow;
    }

    public boolean canFire() {
        return canFire.get();
    }
}
